#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Mythread.hpp"
#include "comm.hpp"

void Usage(std::string proc)
{
	std::cout << "Usage:\n\t" << proc << "server_ip" << " server_port\n"
			  << std::endl;
}

int Initclient(std::string &serverip, uint16_t serverport, struct sockaddr_in *server)
{
	// 1. 创建socket
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		std::cerr << "socket error" << std::endl;
		return -1;
	}
	// 2. client要不要bind？一定要，client也要有自己的IP和PORT。要不要显式[和server一样用bind函数]的bind？不能！不建议！！
	// a. 如何bind呢？udp client首次发送数据的时候，OS会自己自动随机的给client进行bind --- 为什么？防止client port冲突。要bind，必然要和port关联！
	// b. 什么时候bind呢？首次发送数据的时候

	// 构建目标主机的socket_in信息
	memset(server, 0, sizeof(*server));
	server->sin_family = AF_INET;
	server->sin_port = htons(serverport);
	server->sin_addr.s_addr = inet_addr(serverip.c_str());

	return sockfd;
}


//聊天模型就是从客户端发送给服务器，然后再由服务器转发给别的客户端
//所有被多人访问其实就是发送都是发给服务器，最后由服务器进行转发，再通过重定向流的方式就可以输出到另一个设备中
void Recvmessage(int sockfd,std::string name)		//接收到服务器发来的就行
{
	// int fd = openDev("/dev/pts/4", O_WRONLY);
	while(true)
	{
		// 接收
		struct sockaddr_in peer;
		socklen_t len = sizeof(peer);
		char buffer[1024];
		ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
		if (n > 0)
		{
			buffer[n] = 0;
			//./client 127.0.0.1 8888 2>/dev/pts/4,因为输出在标准错误流中，然后把标准错误流重定向到/dev/pts/4，也可以实现效果
			fprintf(stderr, "[%s]%s\n", name.c_str(), buffer); // stderr
			// write(fd, buffer, strlen(buffer));
		}
	}
}

void Sendmessage(int sockfd, struct sockaddr_in &server, std::string name)	//要先发给服务器
{
	std::string message;
	while(true)
	{
		// 发送
		// std::cout << "Please Enter##";
		printf("%s | Enter#", name.c_str());
		fflush(stdout);
		std::getline(std::cin, message);
		sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
	}
}

int main(int argc, char *argv[])
{
	if (argc != 3)
	{
		Usage(argv[0]);
		exit(1);
	}

	std::string serverip = argv[1];
	uint16_t serverport = std::stoi(argv[2]);

	// 初始化客户端
	struct sockaddr_in serveraddr;
	int sockfd = Initclient(serverip, serverport, &serveraddr);
	if (sockfd < 0) return -1;

	Mythread::func_t r = std::bind(&Recvmessage,sockfd,std::placeholders::_1);
	Mythread::func_t s = std::bind(&Sendmessage,sockfd,serveraddr,std::placeholders::_1);

	Mythread::Thread recver(r,"recver");
	Mythread::Thread sender(s,"sender");

	recver.start();
	sender.start();

	recver.join();
	sender.join();
	return 0;
}